package com.gitee.qdbp.base.tags;

import java.io.File;
import java.util.Map;
import java.util.Properties;
import java.util.concurrent.ConcurrentHashMap;
import javax.servlet.http.HttpServletRequest;
import org.springframework.web.servlet.tags.RequestContextAwareTag;
import com.gitee.qdbp.base.tags.TagSettings.PrependType;
import com.gitee.qdbp.base.tags.TagSettings.Setting;
import com.gitee.qdbp.base.utils.WebTools;
import com.gitee.qdbp.tools.files.PathTools;
import com.gitee.qdbp.tools.utils.PropertyTools;
import com.gitee.qdbp.tools.utils.VerifyTools;

/**
 * BaseUrl标签
 *
 * @author zhaohuihua
 * @version 150226
 * @see TagSettings
 */
public class BaseUrlTag extends RequestContextAwareTag {

    /** 版本序列号 **/
    private static final long serialVersionUID = 8169290379157201987L;

    /** 变量名, 如果为空直接将路径输出到页面, 否则将路径保存到变量中 **/
    private String var;
    /** 链接地址, 如果是JS或CSS, 会在链接地址后面加上版本号 **/
    private String href;
    /** 文件名后缀, 如jpg/png等; 或特殊类型: RelativePath/AbsolutePath/SiteFullPath **/
    private String type;
    /** 版本号 **/
    private String version;

    /** 设置变量名 **/
    public void setVar(String var) {
        this.var = var;
    }

    /** 设置链接地址 **/
    public void setHref(String href) {
        this.href = href;
    }

    /** 文件名后缀, 如jpg/png等 **/
    public void setType(String type) {
        this.type = type;
    }

    /** 版本号 **/
    public void setVersion(String version) {
        this.version = version;
    }

    /** 标签处理方法 **/
    @Override
    public int doStartTagInternal() throws Exception {

        HttpServletRequest request = (HttpServletRequest) pageContext.getRequest();

        // 拆分href
        String hrefUri = null;
        String paramString = null;
        String hashString = null;
        if (VerifyTools.isBlank(href)) {
            hrefUri = "/";
        } else {
            int paramIndex = href.indexOf('?');
            int hashIndex = href.indexOf('#');
            if (paramIndex < 0 && hashIndex < 0) { // xxx/index.html
                hrefUri = href;
            } else if (hashIndex < 0) { // xxx/index.html?a=1&b=2
                hrefUri = href.substring(0, paramIndex);
                paramString = href.substring(paramIndex);
            } else if (paramIndex < 0) { // xxx/index.html#c=3
                hrefUri = href.substring(0, hashIndex);
                hashString = href.substring(hashIndex);
            } else { // paramIndex >= 0 && hashIndex >= 0
                if (hashIndex < paramIndex) { // xxx/index.html#c=3?a=1&b=2
                    hrefUri = href.substring(0, hashIndex);
                    hashString = href.substring(hashIndex);
                } else { // xxx/index.html?a=1&b=2#c=3
                    hrefUri = href.substring(0, paramIndex);
                    paramString = href.substring(paramIndex, hashIndex);
                    hashString = href.substring(hashIndex);
                }
            }
        }

        Setting setting;
        // 处理特殊类型
        if (PrependType.RelativePath.name().equals(type)) {
            setting = new Setting(PrependType.RelativePath);
        } else if (PrependType.AbsolutePath.name().equals(type)) {
            setting = new Setting(PrependType.AbsolutePath);
        } else if (PrependType.SiteFullPath.name().equals(type)) {
            setting = new Setting(PrependType.SiteFullPath);
        } else {
            TagSettings settings = getTagSettings(request);
            // 获取href的后缀
            String hrefExtension = VerifyTools.isNotBlank(type) ? type : settings.getUriExtension(hrefUri);
            // 获取当前请求的serverType
            String serverType = settings.getServerType(request);
            // 根据当前请求的服务类型及href后缀获取设置信息
            setting = settings.getSetting(serverType, hrefExtension);
        }

        String prefix;
        switch (setting.getPrependType()) {
        case AbsolutePath:
            prefix = getAbsolutePath(request, hrefUri);
            break;
        case SiteFullPath:
            prefix = getSiteFullPath(request, hrefUri);
            break;
        case StaticString:
            prefix = PathTools.concat(setting.getPrependString(), hrefUri);
            break;
        default:
            prefix = getRelativePath(request, hrefUri);
            break;
        }

        StringBuilder buffer = new StringBuilder();
        buffer.append(prefix);
        if (VerifyTools.isNotBlank(paramString)) {
            buffer.append(paramString);
        }
        switch (setting.getAppendType()) {
        case Version:
            buffer.append(VerifyTools.isBlank(paramString) ? "?" : "&");
            if (VerifyTools.isBlank(version)) {
                buffer.append(version);
            } else {
                buffer.append(globalVersion);
            }
            break;
        case LastModified:
            buffer.append(VerifyTools.isBlank(paramString) ? "?" : "&");
            if (VerifyTools.isNotBlank(version)) {
                buffer.append(version);
            } else {
                buffer.append(getLastModified(request, hrefUri));
            }
            break;
        case StaticString:
            buffer.append(VerifyTools.isBlank(paramString) ? "?" : "&");
            buffer.append(setting.getAppendString());
            break;
        default:
            break;
        }
        if (VerifyTools.isNotBlank(hashString)) {
            buffer.append(hashString);
        }

        if (var == null) { // 没有指定var, 直接输出
            TagTools.print(pageContext.getOut(), buffer.toString());
            return SKIP_BODY;
        } else { // 指定了var, 将路径保存到值栈中
            pageContext.setAttribute(var, buffer.toString());
            return EVAL_PAGE;
        }
    }

    private static String getAbsolutePath(HttpServletRequest request, String hrefUri) {
        String root = VerifyTools.nvl(request.getContextPath(), "/");
        return PathTools.concat(root, hrefUri);
    }

    private static String getRelativePath(HttpServletRequest request, String hrefUri) throws Exception {
        String requestUri = (String) request.getAttribute("OriginalRequestURI");
        if (requestUri == null) {
            throw new Exception("RelativePath must be use SaveRequestDataFilter");
        }
        return PathTools.relativize(requestUri, PathTools.concat(request.getContextPath(), hrefUri));
    }

    private static String getSiteFullPath(HttpServletRequest request, String hrefUri) {
        StringBuilder buffer = new StringBuilder();
        buffer.append(request.getScheme()).append("://");
        buffer.append(request.getServerName());
        buffer.append(":").append(request.getServerPort());
        buffer.append("/").append(request.getContextPath()).append("/");
        return PathTools.concat(buffer.toString(), hrefUri);
    }

    /** 如果版本号有变化则重新加载数据 **/
    private static String globalVersion;
    /** 配置内容缓存 **/
    private static TagSettings settings;

    private TagSettings getTagSettings(HttpServletRequest request) {
        Properties setting = new WebTools(request).getContextProperties();
        String version = PropertyTools.getString(setting, "assets.version");
        if (settings == null || globalVersion == null || !globalVersion.equals(version)) {
            settings = new TagSettings(setting);
            globalVersion = version;
        }
        return settings;
    }

    /** 缓存文件的修改时间 **/
    private static Map<String, Long> LAST_MODIFIED = new ConcurrentHashMap<>();

    public static void clearCachedLastModified() {
        LAST_MODIFIED.clear();
    }

    public static void removeCachedLastModified(String uri) {
        LAST_MODIFIED.remove(uri);
    }

    private String getLastModified(HttpServletRequest request, String hrefUri) {
        if (LAST_MODIFIED.containsKey(hrefUri)) {
            long lastModified = LAST_MODIFIED.get(hrefUri);
            return lastModified < 0 ? globalVersion : String.valueOf(lastModified);
        } else {
            try {
                String realPath = request.getServletContext().getRealPath(hrefUri);
                File realFile = new File(realPath);
                if (realFile.exists()) {
                    long lastModified = realFile.lastModified();
                    LAST_MODIFIED.put(hrefUri, lastModified);
                    return String.valueOf(lastModified);
                } else {
                    LAST_MODIFIED.put(hrefUri, -1L);
                }
            } catch (Exception e) {
                LAST_MODIFIED.put(hrefUri, -2L);
            }
            return globalVersion;
        }
    }

}
